#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
#include <dirent.h>
#include <sys/time.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <pwd.h>

#include "data_type.h"
#include "cube.h"
#include "cube_define.h"
#include "cube_record.h"

#include "json.h"

#include "shell_list.h"
#include "cmdlist_run.h"

#define MAX_LINE_LEN 1024

static unsigned char Buf[DIGEST_SIZE*32];
const int max_para = 10;

int cmdlist_run_init(void * sub_proc,void * para)
{
    int ret;

    return 0;
}

int cmdlist_run_start(void * sub_proc,void * para)
{
    		
    int ret = 0, len = 0, i = 0, j = 0;
    int rc = 0;
    struct start_para * start_para = para;

    int type,subtype;	
	int init_state;

    void * recv_msg;



	ret=ex_module_recvmsg(sub_proc,&recv_msg);
	if(ret<0)
		return ret;
	if(recv_msg==NULL)
		return 0;
	type=message_get_type(recv_msg);
	subtype=message_get_subtype(recv_msg);
	if(!memdb_find_recordtype(type,subtype))
	{
		printf("message format (%d %d) is not registered!\n",
		message_get_type(recv_msg),message_get_subtype(recv_msg));
		ex_module_sendmsg(sub_proc,recv_msg);
		return 0;
	}
	if((type==TYPE(GENERAL))&&(subtype==SUBTYPE(GENERAL,STRING)))
	{
		ret=proc_cmdlist_run(sub_proc,recv_msg);
	}
	else
	{
		ret=ex_module_sendmsg(sub_proc,recv_msg);
		return 0;
	}
    return ret;
}

int proc_cmdunit_run(RECORD(SHELL_LIST,CMD_UNIT) * cmd_unit)
{
    pid_t fpid; //fpid表示fork函数返回的值  
    int count=0;  
	int ret;
	char ** para_name;
	char * new_cmd_line;
	int para_num;

	para_name = Talloc0(max_para * sizeof(char *));
	if(para_name == NULL)
		return -ENOMEM;
			
	para_num = cmdunit_get_para(cmd_unit, para_name);
	printf(" enter child fork, para_num %d para %s \n",para_num,para_name[0]);
	if(para_num <0)
		return -EINVAL;
	if(para_num == 0)
	{			
		fpid=fork();   
		if (fpid < 0) 
		{
			print_cubeerr("error in fork!");  
			return -EINVAL;
		}

		if (fpid == 0) {  
			int status;
			status = system(cmd_unit->cmd_line);
			if (-1 == status)
			{
					print_cubeerr("cmdlist_run: system run %s error!",cmd_unit->cmd_line);
					exit(-EINVAL);
			}
			else
			{
				if (WIFEXITED(status))
				{
					if (0 == WEXITSTATUS(status))
					{
						exit(0);
					}
					print_cubeerr("cmdlist_run: run shell script %s fail, script exit code: %d\n", 
						cmd_unit->cmd_line,WEXITSTATUS(status));
					exit(-EINVAL);
				}
				print_cubeerr("cmdlist_run: shell script %s exit fail, script exit code: %d\n", 
					cmd_unit->cmd_line,WEXITSTATUS(status));
				exit(-EINVAL);
			}
			exit(0);
		}	  
		else
		{
			print_cubeaudit("cmdlist_run: prepare run %s !",cmd_unit->cmd_line);
		}
	}
	else // para_num >0, we need replace para
	{
		printf(" get para %d %s ...!\n",para_num,para_name[0]);
				// find all the para value first
		char ** para_array;
		int i;
		int offset=0;
		int status;
	    para_array = Talloc0(para_num*sizeof(char *));
		if(para_array == NULL)
				return -ENOMEM;
		for(i=0;i<para_num;i++)
		{
			ret = cmdunit_get_para_value(cmd_unit,para_name[i],&para_array[i]);
			if(ret<0)
			{
				printf("get para value failed! ret %d\n",ret);
				return ret;
			}
		}	

		printf("get para value %s ...\n",para_array[0]);

		char * new_cmd_line;
		new_cmd_line = Talloc0(DIGEST_SIZE*32);
		Strcpy(new_cmd_line,cmd_unit->cmd_line);
        char * name_string;
		name_string = Talloc0(DIGEST_SIZE*2+1);
		for(i=0;i<para_num;i++)
		{
			name_string[0]='$';
			name_string[1]='(';
			Strcpy(name_string+2,para_name[i]);
			Strcat(name_string,")");
			ret = replace_substring(new_cmd_line,DIGEST_SIZE*32,name_string,DIGEST_SIZE*2,para_array[i],DIGEST_SIZE*8);
		}
		Free0(name_string);
		fpid=fork();   
		if (fpid < 0) 
		{
			print_cubeerr("error in fork!");  
			return -EINVAL;
		}

		if (fpid == 0) {  
			int status;
			status = system(new_cmd_line);
			if (-1 == status)
			{
					print_cubeerr("cmdlist_run: system run %s error!",new_cmd_line);
					exit(-EINVAL);
			}
			else
			{
				if (WIFEXITED(status))
				{
					if (0 == WEXITSTATUS(status))
					{
						exit(0);
					}
					print_cubeerr("cmdlist_run: run shell script %s fail, script exit code: %d\n", 
						cmd_unit->cmd_line,WEXITSTATUS(status));
					exit(-EINVAL);
				}
				print_cubeerr("cmdlist_run: shell script %s exit fail, script exit code: %d\n", 
					cmd_unit->cmd_line,WEXITSTATUS(status));
				exit(-EINVAL);
			}
			exit(0);
		}	  
		else
		{
			print_cubeaudit("cmdlist_run: prepare run %s !",new_cmd_line);
		}
		for(i=0;i<para_num;i++)
		{
			Free0(para_name[i]);
			Free0(para_array[i]);
		}
		Free0(para_name);
		Free0(para_array);
		Free0(new_cmd_line);
	}

     if(cmd_unit->delay>0)
         usleep(cmd_unit->delay);
     return 0;
}

int proc_cmdset_run(void * sub_proc, RECORD(SHELL_LIST,CMD_SET) * cmd_set, void *recv_msg)
{
	int ret;
	int i;
	void * send_msg;
	DB_RECORD * db_record;
	int unit_seq=0;
	RECORD(SHELL_LIST,CMD_UNIT) * comp_unit;
	BYTE uuid[DIGEST_SIZE];
	comp_unit = Talloc0(sizeof(*comp_unit));
	if(comp_unit == NULL)
		return -ENOMEM;
	comp_unit->item_name=dup_str(cmd_set->item_name,64);
	comp_unit->set_name=dup_str(cmd_set->set_name,64);

	for(unit_seq=1;unit_seq<= cmd_set->cmd_num;unit_seq++)
	{
		comp_unit->seq_no=unit_seq;
		memdb_comp_record_uuid(comp_unit,TYPE_PAIR(SHELL_LIST,CMD_UNIT),uuid);
		db_record=memdb_find(uuid,TYPE_PAIR(SHELL_LIST,CMD_UNIT));
		if(db_record == NULL)
		{
			print_cubeerr("cmdlist_run: set %s can't find cmd!",cmd_set->set_name);
			return -EINVAL;
		}
		ret = proc_cmdunit_run(db_record->record);
		if(ret<0)
		{
			print_cubeerr("cmdlist_run: run cmd fail!");
			return ret;
		}		
	}

   	RECORD(GENERAL,STRING) * shell_notice;
	shell_notice = Talloc0(sizeof(*shell_notice));
	if(shell_notice == NULL)
		return -ENOMEM;
    shell_notice->value  = dup_str(cmd_set->cmdset_desc,128);
    shell_notice->name = dup_str(cmd_set->item_name,32);
    send_msg=message_create(TYPE_PAIR(GENERAL,STRING),recv_msg);
    if(send_msg==NULL)
        return -EINVAL;
    message_add_record(send_msg,shell_notice);
    ex_module_sendmsg(sub_proc,send_msg);

}

int proc_cmdlist_run(void * sub_proc,void * recv_msg)
{
    int ret;
    int i=0;

    RECORD(SHELL_LIST,CMD_SET) * comp_set;
    RECORD(GENERAL,STRING) * cmd_item;
    DB_RECORD * db_record;
    int seq_no=0;
    BYTE uuid[DIGEST_SIZE];


    ret = message_get_record(recv_msg,&cmd_item,0);
    if(ret<0)
        return ret;

	comp_set = Talloc0(sizeof(*comp_set));
	if(comp_set == NULL)
		return -ENOMEM;
	comp_set->item_name=dup_str(cmd_item->value,64);

	comp_set->seq_no=1;
	
    do{
		memdb_comp_record_uuid(comp_set,TYPE_PAIR(SHELL_LIST,CMD_SET),uuid);
		db_record=memdb_find(uuid,TYPE_PAIR(SHELL_LIST,CMD_SET));
		if(db_record == NULL)
			break;
		proc_cmdset_run(sub_proc,db_record->record,recv_msg);
		comp_set->seq_no++;

	}while(1);

	return comp_set->seq_no;
    return 0; 
}

int find_right_branch(char * Buf,int size,char right_branch);

int cmdunit_get_para(RECORD(SHELL_LIST,CMD_UNIT) * cmd_unit,char ** para_list)
{
	int para_num=0;
	char * buffer;
	int cmd_size,paraname_size;
	int i,j;
	cmd_size = Strlen(cmd_unit->cmd_line);
	if(cmd_size<=0)
		return -EINVAL;
	for(i=0;i<cmd_size;i++)
	{
		if(cmd_unit->cmd_line[i] == '\\')
		{
			i++;
			continue;
		}
		if(cmd_unit->cmd_line[i] == '$')
		{
			if(cmd_unit->cmd_line[i+1] == '(')
			{
				paraname_size = find_right_branch(cmd_unit->cmd_line+i+2,cmd_size-i,')');
				if(paraname_size <=0)
					return -EINVAL;


				if(para_num+1 >= max_para)
				{
					print_cubeerr("too many para in cmd_line");
					return -EINVAL;
				}
				para_list[para_num]=Talloc0(paraname_size+1);
				Memcpy(para_list[para_num],cmd_unit->cmd_line+i+2,paraname_size);
				i+=paraname_size+2;  // paraname, ( and )
				for(j=0;j<para_num;j++)
				{
					if(Strcmp(para_list[para_num],para_list[j])==0)
					{
						Free0(para_list[para_num]);
						para_list[para_num]=NULL;
						para_num--;
						break;		
					}	
				}		
				para_num++;
			}
		}
	}
	return para_num;	
}

int cmdunit_get_para_value(RECORD(SHELL_LIST,CMD_UNIT) *cmd_unit, char * para_name, char ** para_value)
{
	RECORD(SHELL_LIST,CMD_PARA) * para_index;
	RECORD(SHELL_LIST,CMD_PARA) * para_store;
	BYTE index_uuid[DIGEST_SIZE];
	int ret;
	para_index->set_name = dup_str(cmd_unit->set_name,DIGEST_SIZE*2+1);
	para_index->item_name = dup_str(cmd_unit->item_name,DIGEST_SIZE*2+1);
	para_index->para_name = dup_str(para_name,DIGEST_SIZE*2+1);
	// try the total 
	ret = memdb_comp_record_uuid(para_index,TYPE_PAIR(SHELL_LIST,CMD_PARA),index_uuid);
	if(ret<0)
	{
		return ret;
	}
	DB_RECORD * db_record;
	db_record = memdb_find(index_uuid,TYPE_PAIR(SHELL_LIST,CMD_PARA));
	if(db_record == NULL)
	{
		if(cmd_unit->set_name != NULL)
		{
			Free0(para_index->set_name);
			para_index->set_name=dup_str("",0);
			ret = memdb_comp_record_uuid(para_index,TYPE_PAIR(SHELL_LIST,CMD_PARA),index_uuid);
			if(ret<0)
			{
				return ret;
			}
			db_record = memdb_find(index_uuid,TYPE_PAIR(SHELL_LIST,CMD_PARA));
		}
		if(db_record == NULL)
		{
			if(cmd_unit->item_name != NULL)
			{
				Free0(para_index->item_name);
				para_index->item_name=dup_str("",0);
				ret = memdb_comp_record_uuid(para_index,TYPE_PAIR(SHELL_LIST,CMD_PARA),index_uuid);
				if(ret<0)
				{
					return ret;
				}
				db_record = memdb_find(index_uuid,TYPE_PAIR(SHELL_LIST,CMD_PARA));
			}
		}
	}
	if(db_record !=NULL)
	{
		para_store = db_record->record;
		*para_value = dup_str(para_store->para_value,DIGEST_SIZE*8);
		printf("Get para %s value %s\n",para_name,*para_value);
		return 1;
	}
	return 0;
}

int find_left_branch(char * Buf,int size,char left_branch)
{
	int i;
	for(i=0;i<size;i++)
	{
		if(Buf[i] == 0)
			return 0;

		if(Buf[i] == left_branch)
			break;
	}
	return i;
}

int find_right_branch(char * Buf,int size,char right_branch)
{
	int i;
	int deep=1;
	int is_in_comma=0;
	char left_branch;
	if(right_branch == '}') 
	{
		left_branch ='{';
	}
	else if(right_branch == ']') 
	{
		left_branch ='[';
	}
	else if(right_branch == ')') 
	{
		left_branch ='(';
	}
	else
		return -EINVAL;

	for(i=0;i<size;i++)
	{
		if(Buf[i] == 0)
			return 0;
		if(Buf[i]=='\\')
		{
			i++;
			if(Buf[i]==0)
				return 0;
		}

		if(Buf[i] == '\"')
		{
			if(is_in_comma)
				is_in_comma=0;
			else
				is_in_comma=1;
		}
		if(is_in_comma == 0)
		{
			if(Buf[i]==left_branch)
				deep++;
			else if(Buf[i]==right_branch)
				deep--;
		}
		if(deep==0)
			break;

	}
	if(Buf[i]!=right_branch)
		return -1;
	return i;
}

int __move_string_tail(char * string, int maxstrlen, int site,int new_site)
{
	int strlen;
	int i;

	if(maxstrlen==0)
		strlen=Strlen(string);
	else
		strlen=Strnlen(string,maxstrlen);
	if(site==new_site)
		return strlen;

	if(site<new_site)
	{
		i=strlen;
		int j=i+new_site-site;
		if(j>maxstrlen)
			return -EINVAL;

		if(j<maxstrlen)
			string[j]=0;
		
		for(;i>=site;i--,j--)
			string[j-1]=string[i-1];
	}
	else
	{
		for(i=site;i<=strlen;i++)
			string[i+new_site-site]=string[i];
		if(strlen<maxstrlen)
			string[strlen]=0;
	}
	return strlen;
}

int replace_substring(char * string, int maxstrlen,char * substring,int maxsublen,char * replacestring,int maxreplacelen)
{
	int ret;
	int i;
	int strlen,substrlen,replacelen;
	if(maxstrlen==0)
		strlen=Strlen(string);
	else
		strlen=Strnlen(string,maxstrlen);
	if(strlen<0)
		return strlen;

	if(maxsublen==0)
		substrlen=Strlen(substring);
	else
		substrlen=Strnlen(substring,maxsublen);
	if(substrlen<0)
		return substrlen;

	if(maxreplacelen==0)
		replacelen=Strlen(replacestring);
	else
		replacelen=Strnlen(replacestring,maxreplacelen);
	if(replacelen<0)
		return replacelen;

	if(strlen<substrlen)
		return 0;

	for(i=0;i<=strlen-substrlen;i++)
	{
		if(Memcmp(string+i,substring,substrlen)==0)
		{
			strlen=__move_string_tail(string,maxstrlen,i+substrlen,i+replacelen);
			if(strlen>maxstrlen)
				return -EINVAL;
			Memcpy(string+i,replacestring,replacelen);
			i+=replacelen-1;
		}
	}
	return strlen;
} 
